/**
 * Copyright (c) 2015-2017, Henry Yang 杨勇 (gismail@foxmail.com).
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.lambkit.generator.impl;

import com.lambkit.core.Lambkit;
import com.lambkit.db.dialect.IDialect;
import com.lambkit.db.meta.TableMeta;
import com.lambkit.db.mgr.MgrConstants;
import com.lambkit.db.mgr.MgrTable;
import com.lambkit.db.mgr.MgrdbService;
import com.lambkit.generator.DataSourceMetaBuilder;
import com.lambkit.generator.BaseGenerator;
import com.lambkit.generator.GeneratorConfig;
import com.lambkit.util.Printer;

import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class DatabaseGenerator extends BaseGenerator {

	private Map<String, TableMeta> tableMetas = null;

	@Override
	public void context(DataSource dataSource, Map<String,Object> options, GeneratorConfig config) {
		super.context(dataSource, options, config);
		this.tableMetas = creataTableMetas(options);
	}

	@Override
	public void generate(String templateFolder, String templatePath, Map<String, Object> options) {
		// TODO Auto-generated method stub
		if(context==null) {
			Printer.print(this, "generator", getClass() + " Generator context is null, please call context() first.");
			return;
		}
		if(tableMetas==null) {
			Printer.print(this, "generator", getClass() + " Generator tableMetas is null, please call context() first.");
			return;
		}
		boolean hasMgrTable = false;
		boolean genMgrTable = true;
		if(options.containsKey("hasMgrTable") && "true".equals(options.get("hasMgrTable").toString())) {
			hasMgrTable = true;
		}
		if(options.containsKey("genMgrTable") && "false".equals(options.get("genMgrTable").toString())) {
			genMgrTable = false;
		}
		if(!context.getConfig().isHasMgrdb()) {
			hasMgrTable = false;
		}
		boolean eachTable = options.containsKey("eachTable") && "false".equals(options.get("eachTable").toString()) ? false : true;
		if(eachTable) {
			for (TableMeta tableMeta : tableMetas.values()) {
				String tableName = tableMeta.getName();
				if(!genMgrTable) {
					if(tableName.startsWith("meta_") ||
							"sys_tableconfig".equals(tableName) ||
							"sys_fieldconfig".equals(tableName)) {
						continue;
					}
				}
				Map<String, Object> templateModel = createTemplateModel(options, tableMeta);
				templateModel.put("tables", tableMetas.values());
				context.generate(templateModel, templateFolder, templatePath);
			}
		} else {
			List<Map<String, Object>> tables = new ArrayList<Map<String, Object>>();
			for (TableMeta tableMeta : tableMetas.values()) {
				String tableName = tableMeta.getName();
				if(!genMgrTable) {
					if(tableName.startsWith("meta_") ||
							"sys_tableconfig".equals(tableName) ||
							"sys_fieldconfig".equals(tableName)) {
						continue;
					}
				}
				Map<String, Object> table = createTemplateModel(options, tableMeta);
				tables.add(table);
			}
			Map<String, Object> templateModel = context.createTemplateModel(options);
			templateModel.put("tables", tableMetas.values());
			templateModel.put("tablelist", tables);
			context.generate(templateModel, templateFolder, templatePath);
		}
		
	}

	@Override
	public void generate(String templateFolder, String templateFilePath, String generatFilePath, Map<String, Object> options) {
		if(context==null) {
			Printer.print(this, "generator", getClass() + " Generator context is null, please call context() first.");
			return;
		}
		if(tableMetas==null) {
			Printer.print(this, "generator", getClass() + " Generator tableMetas is null, please call context() first.");
			return;
		}
		boolean hasMgrTable = false;
		boolean genMgrTable = true;
		if(options.containsKey("hasMgrTable") && "true".equals(options.get("hasMgrTable").toString())) {
			hasMgrTable = true;
		}
		if(options.containsKey("genMgrTable") && "false".equals(options.get("genMgrTable").toString())) {
			genMgrTable = false;
		}
		if(!context.getConfig().isHasMgrdb()) {
			hasMgrTable = false;
		}
		boolean eachTable = options.containsKey("eachTable") && "false".equals(options.get("eachTable").toString()) ? false : true;
		if(eachTable) {
			for (TableMeta tableMeta : tableMetas.values()) {
				String tableName = tableMeta.getName();
				if(!genMgrTable) {
					if(tableName.startsWith("meta_") ||
							"sys_tableconfig".equals(tableName) ||
							"sys_fieldconfig".equals(tableName)) {
						continue;
					}
				}
				Map<String, Object> templateModel = createTemplateModel(options, tableMeta);
				templateModel.put("tables", tableMetas.values());
				context.generate(templateModel, templateFolder, templateFilePath, generatFilePath);
			}
		} else {
			List<Map<String, Object>> tables = new ArrayList<Map<String, Object>>();
			for (TableMeta tableMeta : tableMetas.values()) {
				String tableName = tableMeta.getName();
				if(!genMgrTable) {
					if(tableName.startsWith("meta_") ||
							"sys_tableconfig".equals(tableName) ||
							"sys_fieldconfig".equals(tableName)) {
						continue;
					}
				}
				Map<String, Object> table = createTemplateModel(options, tableMeta);
				tables.add(table);
			}
			Map<String, Object> templateModel = context.createTemplateModel(options);
			templateModel.put("tables", tableMetas.values());
			templateModel.put("tablelist", tables);
			context.generate(templateModel, templateFolder, templateFilePath, generatFilePath);
		}
	}
	
	@Override
	public Object execute(String templateFolder, String templateFilePath, Map<String, Object> options) {
		// TODO Auto-generated method stub
		if(context==null) {
			return null;
		}
		Map<String, Object> templateModel = context.createTemplateModel(options);
		templateModel.put("tables", tableMetas.values());
		templateModel.putAll(options);
		return context.execute(options, templateFolder, templateFilePath);
	}

	public Map<String, TableMeta> getTableMetas() {
		return tableMetas;
	}

	public void setTableMetas(Map<String, TableMeta> tableMetas) {
		this.tableMetas = tableMetas;
	}

	public Map<String, Object> createTemplateModel(Map<String, Object> options, TableMeta tableMeta) {
		Map<String, Object> templateModel = context.createTemplateModel(options);
		boolean hasMgrTable = false;
		boolean genMgrTable = true;
		if(options.containsKey("hasMgrTable") && "true".equals(options.get("hasMgrTable").toString())) {
			hasMgrTable = true;
		}
		if(options.containsKey("genMgrTable") && "false".equals(options.get("genMgrTable").toString())) {
			genMgrTable = false;
		}
		if(!context.getConfig().isHasMgrdb()) {
			hasMgrTable = false;
		}
		String tableName = tableMeta.getName();
		templateModel.put("table", tableMeta);
		templateModel.put("modelName", tableMeta.getModelName());
		templateModel.put("classname", tableMeta.getModelName());
		templateModel.put("attrName", tableMeta.getAttrName());
		templateModel.put("columns", tableMeta.getColumnMetas());
		templateModel.put("tableName", tableMeta.getName());
		templateModel.put("tablename", tableMeta.getName());
		templateModel.put("primaryKey", tableMeta.getPrimaryKey());
		templateModel.put("title", tableMeta.getTitle());
		if(hasMgrTable) {
			MgrTable mgrtb = Lambkit.get(MgrdbService.class).createTableWithoutMeta(tableName, MgrConstants.ALL, null);
			templateModel.put("model", mgrtb);
			templateModel.put("fieldList", mgrtb.getFieldList());
			if(mgrtb!=null && mgrtb.getModel()!=null) {
				templateModel.put("title", mgrtb.getModel().getTitle());
			}
		}
		return templateModel;
	}

	public Map<String, TableMeta> creataTableMetas(Map<String, Object> options) {
		DataSource dataSource = getContext().getDataSource();
		IDialect dialect = getContext().getDialect();
		if(dataSource==null) {
			Printer.print(this, "generator", getClass() + " DataSource is null, please check your config file.");
		}
		if(dialect==null) {
			Printer.print(this, "generator", getClass() + " dialect is null, please check your config file.");
		}
		if(dataSource==null) {
			Printer.print(this, "generator", getClass() + " DataSource is null, please check your config file.");
			return null;
		}
		DataSourceMetaBuilder metaBuilder = new DataSourceMetaBuilder(dataSource);
		metaBuilder.setDialect(dialect);
		if(options.containsKey("tableRemovePrefixes")) {
			Object trp = options.get("tableRemovePrefixes");
			if(trp instanceof List) {
				List<String> tableRemovePrefixes = (List<String>) trp;
				metaBuilder.setRemovedTableNamePrefixes((String[]) tableRemovePrefixes.toArray(new String[tableRemovePrefixes.size()]));
			} else {
				String tableRemovePrefixes = trp.toString();
				metaBuilder.setRemovedTableNamePrefixes(tableRemovePrefixes.split(","));
			}
		}
		if(options.containsKey("excludedTables")) {
			Object eto = options.get("excludedTables");
			if(eto instanceof List) {
				List<String> excludedTables = (List<String>) eto;
				metaBuilder.addExcludedTable((String[]) excludedTables.toArray(new String[excludedTables.size()]));
			} else {
				String excludedTables = eto.toString();
				metaBuilder.addExcludedTable(excludedTables.split(","));
			}
		}
		if(options.containsKey("includedTables")) {
			Object eto = options.get("includedTables");
			if(eto instanceof List) {
				List<String> includedTables = (List<String>) eto;
				metaBuilder.addIncludedTable((String[]) includedTables.toArray(new String[includedTables.size()]));
			} else {
				String includedTables = eto.toString();
				metaBuilder.addIncludedTable(includedTables.split(","));
			}
		}
		return metaBuilder.build();
	}
}
